package com.hmk.blogboot.service.Impl;


import com.hmk.blogboot.NotFoundException;
import com.hmk.blogboot.dao.BlogRepository;
import com.hmk.blogboot.po.Blog;
import com.hmk.blogboot.po.Type;
import com.hmk.blogboot.service.BlogService;
import com.hmk.blogboot.util.MarkdownUtils;
import com.hmk.blogboot.util.MyBeanUtils;
import com.hmk.blogboot.vo.BlogQuery;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.util.*;

@Service
public class BlogServiceImpl  implements BlogService {

    //自动注入blog的dao接口
    @Autowired
    private BlogRepository blogRepository;

    //根据id获取blog
    @Override
    @Transactional
    public Blog getBlog(Long id) {
        //blog的dao接口调用getOne(id)根据id查询blog
        return blogRepository.getOne(id);
    }

    /*
    * 实现复杂联合动态查询
    * 1dao接口实现一个接口JpaSpecificationExecutor<Blog>
    * 2在分页查询，dao接口调用findAll(pecification,pageable)
    * pecification用来·联合动态查询
    * */

    @Override
    @Transactional
    public Page<Blog> listBlog(Pageable pageable, BlogQuery blog) {

        return blogRepository.findAll(new Specification<Blog>() {
            //构造复杂动态联合查询
            /*
            * root:查询对像 获取表字段和属性名
            * criteriaQuery:查询条件容器，构造查询条件存放
            * CriteriaBuilder：构造查询条件
            * Predicate：组合条件
            * */
            @Override
            public Predicate toPredicate(Root<Blog> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
                //创建list容器，存放组合条件
                List<Predicate> predicates =new ArrayList<>();
                //如果blog标题不为空字符串和不为空
                if(!"".equals(blog.getTitle()) && blog.getTitle()!=null){
                    //构造组合条件 ：cb对象构造模糊查询 root找到数据库字段，字段值
                    predicates.add(cb.like(root.<String>get("title"),"%"+blog.getTitle()+"%"));
                }
                //如果blog实体类中type属性的类型id不为空，添加到组合条件中
                if(blog.getTypeId()!=null){
                    //构造条件添加到组合条件
                    //cb对象构造存在类型id
                    predicates.add(cb.equal(root.<Type>get("type").get("id"),blog.getTypeId()));
                }

                /*如果推荐为true,添加到组合条件*/
                if(blog.isRecommend()){
                    //构造条件调价到组合条件中predicates对象
                    //表字段 ==实体类属性值
                    predicates.add(cb.equal(root.<Boolean>get("recommend"),blog.isRecommend()));
                }
                //where方法相当于sql where限制条件
                cq.where(predicates.toArray(new Predicate[predicates.size()]));
                return null;
            }
        },pageable/*设置分页参数*/);
    }


    /*保存blog*/
    @Override
    @Transactional
    public Blog saveBlog(Blog blog) {
    //如果保存数据库blog表中的blog对象id为空，没有博客id,
        //blog表中不存在这条blog记录，保存到数据库
        if(blog.getId()==null){
            //设置blog的初始化创建时间
            blog.setCreateTime(new Date());
            //设置blog的初始化更新时间
            blog.setUpdateTime(new Date());
            //设置blog的初始化浏览次数
            blog.setViews(0);
        }else{
            //这篇博客数据库存在
            blog.setUpdateTime(new Date());
        }
        //blog dao接口调用save()保存到数据库
        return blogRepository.save(blog);
    }

    @Override
    @Transactional
    public void deleteBlog(Long id) {
    //根据id进行删除
        blogRepository.deleteById(id);

    }
    @Transactional
    @Override
    public Blog updateBlog(Long id, Blog blog) {
        //使用blog的dao接口调用getOne(id)根据id进行获取blog
        Blog b = blogRepository.getOne(id);
        //如果查询到的blog为空 数据库没有这条blog记录
        //抛出一个自定义找不到异常
        if(b==null){
            throw new NotFoundException("该博客不存在");
        }
        //使用spring 提供的BeanUtils工具类，要修改的blog对象
        //属性复制给查询到的博客对象b
        BeanUtils.copyProperties(blog,b, MyBeanUtils.getNullPropertyNames(blog));
        //设置更新博客的更新时间
        b.setUpdateTime(new Date());
        //把修改后的博客记录保存到数据库blog表中
        return blogRepository.save(b);
    }

    //直接进行分页查询blog数据
    @Override
    public Page<Blog> listBlog(Pageable pageable) {
        return blogRepository.findAll(pageable);
    }

    /*首页展示评论列表*/
    @Override
    public List<Blog> listRecommendBlogTop(Integer size) {
        //创建jpa的排序对象
        Sort sort  = Sort.by( Sort.Direction.DESC,"updateTime");

        //创建Pageable分页对象
        Pageable pageable = PageRequest.of(0,size,sort);
        return blogRepository.findTop(pageable);
    }

    /*输入博客content或title进行搜索 使用模糊匹配查询*/
    @Override
     public Page<Blog> listBlog(String query,Pageable pageable){
        return blogRepository.findByQuery(query,pageable);
     }

     /*博客详情页  根据id获取blog 同时浏览次数+1*/
    @Override
    @Transactional
    public Blog getAndConvert(Long id) {
        //根据id查询获取blog对象
        Blog blog = blogRepository.getOne(id);
        //判断根据id 查询获取的blog 在数据库有没有这条数据
        //如果没有
        if(blog == null){
            //抛出一个自定义找不到异常
            throw new NotFoundException("该博客不存在");
        }
        //修改浏览次数加一 这里我觉都有bug,移到异常下面，看看浏览次数是否有+1
        blogRepository.updateViews(id);
        //初始化一个空的blog对象
        Blog b = new Blog();
        //使用spring提供的BeanUtils工具类进行 查询到的blog对象属性复制 初始化blog对象
        BeanUtils.copyProperties(blog,b);
        //获取blog 里面内容
        String content = b.getContent();
        //把获取到的b'log内容进行转换成html
        b.setContent(MarkdownUtils.markdownToHtmlExtensions(content));

        return b;
    }

    @Override
    public Page<Blog> listBlog(Long tagId, Pageable pageable) {

        return blogRepository.findAll(new Specification<Blog>() {
            @Override
            public Predicate toPredicate(Root<Blog> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
                    //Join对象进行关联查询 blog 和tags进行关联查询
                Join join = root.join("tags");
                //构造条件，tags表中id ==tagId
                return cb.equal(join.get("id"),tagId);
            }
        },pageable);

    }


//    按照年份归档显示博客列表
    @Override
    public Map<String, List<Blog>> archiveBlog() {
        //按照年份排序获取年份的列表
        List<String>  years = blogRepository.findGroupYear();

        //创建一个空的map容器
        Map<String,List<Blog>>  map = new HashMap<>();
        //循环遍历年份列表    做把年份作为map的key 根据年份查询到的blog列表作为value值 key-value键值对添加到容器中
        for(String  year : years){
            map.put(year,blogRepository.findByYear(year));
        }

        return map;
    }

    /*获取blog表的条数*/
    @Override
    public Long countBlog() {
        return blogRepository.count();
    }
}
